﻿//Nagappan

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace Tzync
{
    /**************************************
    /*Class to handle filters          
      Description: 1.Adds/Removes Path Filters and Wildcard Filters (Unique for each Profile)   
                   2.Checks if a file is satisfied by the filters.
                   3.Reads/Writes Filters Metadata for persistency on the other computer
      Written By : N Nagappan             */
    /**************************************/

    class Filter
    {

        private List<String> includePathFilters,excludePathFilters;
        private List<String> wildCardFilters;
        private bool isPathFilterOn;
        private bool isWildCardFilterOn;
        private FilterIncOrEx _wildCardIncOrEx;
        private Profile profile;

        #region constructor
        public Filter(Profile profile)
        {
            includePathFilters = new List<string>();
            excludePathFilters = new List<string>();
            wildCardFilters= new List<string>();
            isWildCardFilterOn = false;
            isPathFilterOn = false;
            this.profile = profile;
            //FilterIncOrEx regexIncOrEx = FilterIncOrEx.Included;

        }

        #endregion

        #region set get for Wild Cards
        public FilterIncOrEx WildCardIncOrEx
        {
            get { return _wildCardIncOrEx; }
            set { _wildCardIncOrEx = value; }
        }
        #endregion

        #region Add Wild Card Filter
        public void AddWildCardFilter(String s)
        {
             isWildCardFilterOn = true;
             wildCardFilters.Add(s);
        }
        #endregion

        #region Add Path Filter
        /// <summary>
        /// Adds a filter to the include or exclude filter list
        /// </summary>
        /// <param name="filterPath">the path of the filter</param>
        /// <param name="incOrEx">Type of filter {include or exclude}</param>
        public void AddPathFilter(String filterPath,FilterIncOrEx incOrEx)
        {
            isPathFilterOn = true;
            
            filterPath=RemoveRootPath(filterPath);
            
            if (incOrEx == FilterIncOrEx.Included)
            {
                RemoveSubPath(filterPath,includePathFilters); 
                includePathFilters.Add(filterPath);
                RemoveNotIncluded(filterPath, excludePathFilters);
            }

            else
             {
                RemoveSubPath(filterPath,includePathFilters);
                RemoveSubPath(filterPath,excludePathFilters);
                excludePathFilters.Add(filterPath);
             }
        }

        //Removes 
        private void RemoveNotIncluded(string filterPath, List<string> excludePathFilters)
        {
            for(int i=0;i<excludePathFilters.Count;++i)
            {
                if(!IfSubPath(excludePathFilters[i],filterPath))
                {    Remove(excludePathFilters[i],excludePathFilters);--i;}

            }    
        }
        #endregion

        #region Remove redundant filters
        /// <summary>
        /// Remove filters which are already covered by the filterPath
        /// </summary>
        /// <param name="filterPath">Super Path</param>
        /// <param name="list">list from which redundant filters should be removed</param>
        public void RemoveSubPath(string filterPath,List<string> list)
        {
            for (int i = 0; i < list.Count; ++i)
                {
                    if (IfSubPath(list[i], filterPath))
                    {
                        RemovePathFilter(list[i]);
                        --i;
                    }

                }
        }
        #endregion


        /// <summary>
        /// Removes Root path to get relative path
        /// </summary>
        /// <param name="path">absolute path</param>
        /// <returns>relative path</returns>
        public string RemoveRootPath(string path)
        {
            if (profile.SystemName == SystemType.Host)
                path = path.Substring(profile.RootAddressHost.Length);
            else if(profile.SystemName== SystemType.Guest)
                path = path.Substring(profile.RootAddressGuest.Length);

            return path;
        }

        //Removes a string from the given list of strings
        public void Remove(string s,List<string> list)
        {
            int i = -1;
            foreach (string filter in list)
            {
                ++i;
                if (s == filter)
                {
                    list.RemoveAt(i);
                    return;
                }
            }
        }

        #region Remove Wildcard Filter
        public void RemoveWildCardFilter(string s)
        {
            Remove(s,wildCardFilters);
        }
        #endregion

        #region Remove Path filter
        public void RemovePathFilter(string s)
        {
            Remove(s,includePathFilters);
            Remove(s,excludePathFilters);
        }
        #endregion


        /// <summary>
        /// Checks if the Filter being added is redundant
        /// </summary>
        /// <param name="filterPath">Path filter</param>
        /// <param name="incOrEx">type of filter</param>
        /// <returns>Type of error (none in the case of valid)</returns>
        public FilterIncOrEx CheckIfAlreadyFiltered(string filterPath, FilterIncOrEx incOrEx)
        {
            string targetPath = RemoveRootPath(filterPath);
            
            //To check if it is already excluded
            foreach (string  path in excludePathFilters)
            {
                if (IfSubPath(targetPath,path))
                    return FilterIncOrEx.Excluded;
            }

            
            if (incOrEx==FilterIncOrEx.Excluded && includePathFilters.Count!=0)
            {
                bool flag = true;
                foreach (string path in includePathFilters)
                {
                    if (!IfSubPath(targetPath, path))
                    { flag = false;break;}
                }
                if(flag==false)
                {
                    return FilterIncOrEx.Excluded;
                }
            }

            //To check if already included
            if(incOrEx==FilterIncOrEx.Included)
            foreach(string path in includePathFilters)
            {
                if (IfSubPath(targetPath,path))
                    return FilterIncOrEx.Included;
            }

            //This section is for when ancestors are added 

            return IfInFilters(targetPath);
        }

        //Checks if a filter matches with an existing filter
        private FilterIncOrEx IfInFilters(string targetPath)
        {
            foreach (string path in excludePathFilters)
            {
                if (targetPath==path)
                    return FilterIncOrEx.AlreadyAdded;
            }
            foreach (string path in includePathFilters)
            {
                if (targetPath==path)
                    return FilterIncOrEx.AlreadyAdded;
            }
            return FilterIncOrEx.None;                  //Default Case
        }


        //To check if path1 is a subPath of path2
        private bool IfSubPath(string path1, string path2)
        {
          if(path1.Contains(path2) && (path2 == path1 || path1.ElementAt(path2.Length) == '\\') )
              return true;

          return false;
        }

        //Clears all filters
        public void FlushAllFilters()
        {
            includePathFilters.RemoveRange(0, includePathFilters.Count);
            excludePathFilters.RemoveRange(0, excludePathFilters.Count);
            wildCardFilters.RemoveRange(0, wildCardFilters.Count);
            isWildCardFilterOn = false;
            isPathFilterOn = false;
        }

        #region To check if Folder is valid
        public bool IsValidFolder(string folderPath)
        {
           string targetPath = RemoveRootPath(folderPath);

           return IsValidPath(targetPath);
        }
        #endregion

        #region To check if a File is valid
        /// <summary>
        /// Checks if a file is valid based on filters
        /// </summary>
        /// <param name="fileToBeValidated">file to be checked</param>
        /// <param name="filePath">path of the file</param>
        /// <returns>true if the file is valid,else false</returns>
        public bool IsValidFile(string fileToBeValidated, string filePath)
        {
            string path = RemoveRootPath(filePath);

            if (isWildCardFilterOn == false && isPathFilterOn == false)
                return true;

            if (!IsValidPath(path))
                return false;

            //Checks if the file satisfies the wildcard filters (depends on if the regexFilters are included or excluded)
            if (isWildCardFilterOn)
            {
                bool ret = (WildCardIncOrEx != FilterIncOrEx.Included);

                foreach (string s in wildCardFilters)
                {
                    if (Regex.IsMatch(fileToBeValidated, WildcardToRegex(s)))
                    {
                        return !ret;
                    }
                }
                return ret;
            }

            return true;         // as the path is valid , ispathfilterOn=true and  iswildcardfilteron=false
        }
        #endregion

        #region check if a path is valid
        public bool IsValidPath(string targetPath)
        {
            if(isPathFilterOn==false)
                return true;
            #region check if it the file is excluded 
            foreach (string path in excludePathFilters)
            {
                if(IfSubPath(targetPath,path))
                    return false;
            }
            #endregion

            #region check if the path is included
            foreach (string path in includePathFilters)
            {
                if (IfSubPath(targetPath,path))
                    return true;
            }
            #endregion

            // To check if there are no include filters:--->Then by default true is sent.
            if (isPathFilterOn && includePathFilters.Count == 0)
                return true;

            return false;
        }
        #endregion

        //To convert wildcard expression to regex expression 
        public static string WildcardToRegex(string pattern)
        {
            return "^" + Regex.Escape(pattern).
            Replace("\\*", ".*").
            Replace("\\?", ".") + "$";
        }

        public  List<string> GetIncludePathFilters()
        {
            return includePathFilters;
        }
        public List<string> GetExcludePathFilters()
        {
            return excludePathFilters;
        }
        public List<string> GetWildCardFilters()
        {
            return wildCardFilters;
        }

        #region write to "fiterMetaData.txt"
        /// <summary>
        /// Writes the filters for persistency
        /// Saved in the profile folder
        /// </summary>
        public void WriteToFilterFile()
        {
            string filterMetaDataPath = profile.ProfilePath + "\\filterMetaData.txt";
            
            //delete if the file already exists
            if (File.Exists(filterMetaDataPath))
            {
                File.Delete(filterMetaDataPath);
            }

            FileStream fs = File.Create(filterMetaDataPath);
            fs.Close();

            StreamWriter filterWriter = new StreamWriter(filterMetaDataPath);

            string ToBeWritten = "";

            #region write include filters
            foreach (string path in includePathFilters)
            {
                ToBeWritten += path;
                ToBeWritten += "|";
            }
            if (ToBeWritten.Count() != 0)
                ToBeWritten = ToBeWritten.Substring(0, ToBeWritten.Length - 1);
            filterWriter.WriteLine(ToBeWritten);
            #endregion

            #region write exclude filters
            ToBeWritten = "";
            foreach (string path in excludePathFilters)
            {
                ToBeWritten += path + "|";
            }
            if (ToBeWritten.Count() != 0)
                ToBeWritten = ToBeWritten.Substring(0, ToBeWritten.Length - 1);    
            filterWriter.WriteLine(ToBeWritten);
            #endregion

            #region write wildcard filters
            ToBeWritten = "";
            foreach (string path in wildCardFilters)
            {
                ToBeWritten += path + "|";
            }
            if (ToBeWritten.Count() != 0)
                ToBeWritten = ToBeWritten.Substring(0, ToBeWritten.Length - 1);
            filterWriter.WriteLine(ToBeWritten);

            if(WildCardIncOrEx==FilterIncOrEx.Included)
            filterWriter.WriteLine("include");
            else
            filterWriter.WriteLine("exclude");
            #endregion

            filterWriter.Close();
        }
        #endregion

        #region read from "filterMetaData.txt"
        public void ReadFromFilterFile()
        {
            string filterFileLocation = profile.ProfilePath + "\\filterMetaData.txt";


            if (File.Exists(filterFileLocation))
            {
                FlushAllFilters();
                StreamReader filterReader = new StreamReader(filterFileLocation);

                #region read include filters
                string line = filterReader.ReadLine();
                string[] data = line.Split('|');
                if (data[0] != "")
                    foreach (string s in data)
                    {
                        isPathFilterOn = true;
                        includePathFilters.Add(s);
                    }
                #endregion

                #region read exclude filters
                line = filterReader.ReadLine();
                data = line.Split('|');
                if (data[0] != "")
                    foreach (string s in data)
                    {
                        isPathFilterOn = true;
                        excludePathFilters.Add(s);
                    }
                #endregion

                #region read wildcard filters
                line = filterReader.ReadLine();
                data = line.Split('|');
                if (data[0] != "")
                    foreach (string s in data)
                    {
                        isWildCardFilterOn = true;
                        wildCardFilters.Add(s);
                    }
                line=filterReader.ReadLine();
                if (line == "include")
                    WildCardIncOrEx = FilterIncOrEx.Included;
                else if(line=="exclude")
                    WildCardIncOrEx = FilterIncOrEx.Excluded;
                #endregion

                filterReader.Close();
            }

        }
        #endregion
    }
}
